﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Algorithm.Distance;
using YJKGridTopologyCal.Geometries.Utilities;
using YJKGridTopologyCal.IO;
using YJKGridTopologyCal.Operation.Distance;

namespace YJKGridTopologyCal.Operation.Buffer.Validate
{
	public class BufferDistanceValidator
	{
		public BufferDistanceValidator(IGeometry input, double bufDistance, IGeometry result)
		{
			this._input = input;
			this._bufDistance = bufDistance;
			this._result = result;
		}

		public bool IsValid()
		{
			double num = Math.Abs(this._bufDistance);
			double num2 = 0.012 * num;
			this._minValidDistance = num - num2;
			this._maxValidDistance = num + num2;
			if (this._input.IsEmpty || this._result.IsEmpty)
			{
				return true;
			}
			if (this._bufDistance > 0.0)
			{
				this.CheckPositiveValid();
			}
			else
			{
				this.CheckNegativeValid();
			}
			bool verbose = BufferDistanceValidator.Verbose;
			return this._isValid;
		}

		public string ErrorMessage
		{
			get
			{
				return this._errMsg;
			}
		}

		public Coordinate ErrorLocation
		{
			get
			{
				return this._errorLocation;
			}
		}

		public IGeometry ErrorIndicator
		{
			get
			{
				return this._errorIndicator;
			}
		}

		private void CheckPositiveValid()
		{
			IGeometry boundary = this._result.Boundary;
			this.CheckMinimumDistance(this._input, boundary, this._minValidDistance);
			if (!this._isValid)
			{
				return;
			}
			this.CheckMaximumDistance(this._input, boundary, this._maxValidDistance);
		}

		private void CheckNegativeValid()
		{
			if (!(this._input is IPolygon) && !(this._input is IMultiPolygon) && !(this._input is IGeometryCollection))
			{
				return;
			}
			IGeometry polygonLines = BufferDistanceValidator.GetPolygonLines(this._input);
			this.CheckMinimumDistance(polygonLines, this._result, this._minValidDistance);
			if (!this._isValid)
			{
				return;
			}
			this.CheckMaximumDistance(polygonLines, this._result, this._maxValidDistance);
		}

		private static IGeometry GetPolygonLines(IGeometry g)
		{
			LinearComponentExtracter filter = new LinearComponentExtracter(new List<IGeometry>());
			IList<IGeometry> polygons = PolygonExtracter.GetPolygons(g);
			foreach (IGeometry geometry in polygons)
			{
				geometry.Apply(filter);
			}
			return g.Factory.BuildGeometry(polygons);
		}

		private void CheckMinimumDistance(IGeometry g1, IGeometry g2, double minDist)
		{
			DistanceOp distanceOp = new DistanceOp(g1, g2, minDist);
			this._minDistanceFound = distanceOp.Distance();
			if (this._minDistanceFound < minDist)
			{
				this._isValid = false;
				Coordinate[] array = distanceOp.NearestPoints();
				this._errorLocation = array[1];
				this._errorIndicator = g1.Factory.CreateLineString(array);
				this._errMsg = string.Concat(new object[]
				{
					"Distance between buffer curve and input is too small (",
					this._minDistanceFound,
					" at ",
					WKTWriter.ToLineString(array[0], array[1]),
					" )"
				});
			}
		}

		private void CheckMaximumDistance(IGeometry input, IGeometry bufCurve, double maxDist)
		{
			DiscreteHausdorffDistance discreteHausdorffDistance = new DiscreteHausdorffDistance(bufCurve, input);
			discreteHausdorffDistance.DensifyFraction = 0.25;
			this._maxDistanceFound = discreteHausdorffDistance.OrientedDistance();
			if (this._maxDistanceFound > maxDist)
			{
				this._isValid = false;
				Coordinate[] coordinates = discreteHausdorffDistance.Coordinates;
				this._errorLocation = coordinates[1];
				this._errorIndicator = input.Factory.CreateLineString(coordinates);
				this._errMsg = string.Concat(new object[]
				{
					"Distance between buffer curve and input is too large (",
					this._maxDistanceFound,
					" at ",
					WKTWriter.ToLineString(coordinates[0], coordinates[1]),
					")"
				});
			}
		}

		public static bool Verbose;

		private const double MaxDistanceDiffFrac = 0.012;

		private readonly IGeometry _input;

		private readonly double _bufDistance;

		private readonly IGeometry _result;

		private double _minValidDistance;

		private double _maxValidDistance;

		private double _minDistanceFound;

		private double _maxDistanceFound;

		private bool _isValid = true;

		private string _errMsg;

		private Coordinate _errorLocation;

		private IGeometry _errorIndicator;
	}
}
